與 outputToObservable
相反的是 outputFromObservable
。 此實用程式函數將 Observable
轉換為 OutputrRef
,以便開發人員可以訂閱 (subscribe) 它來使用該值。與 outputToObservable
函數相同,函數位於 rxjs-interop
套件中。
我一直在努力尋找 outputFromObservable
的良好應用,直到 Chau(一位來自越南的 Angular GDE)描述了他如何使用該函數轉換 reactive form
的 valueChanges Observable 為 OutputRef
。而且,outputFromObservable
還可以將 unified control state changed events 轉換為
OutputRef` 進而訂閱。
讓我們來看看第 23 天的相框演示範例。
@Component({
selector: 'app-photo-size-output',
standalone: true,
imports: [ReactiveFormsModule, FormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="formSubmitSub.next()">
<label for="width">
<span>Width: </span>
<input type="number" id="width" name="width" formControlName="width" />
</label>
<label for="height">
<span>Height: </span>
<input type="number" id="height" name="height" formControlName="height" />
</label>
<button type="submit" [disabled]="this.form.invalid">Change photo</button>
</form>
`,
})
export default class AppPhotoSizeOutputComponent {
form = new FormGroup({
width: new FormControl(300, { nonNullable: true, validators: [ Validators.required, Validators.min(30)] }),
height: new FormControl(200, { nonNullable: true, validators: [ Validators.required, Validators.min(30)] }),
});
formSubmitSub = new Subject<void>();
isBigPortrait = outputFromObservable(this.form.controls.width.valueChanges
.pipe(
debounceTime(300),
map((v) => v >= 700),
));
}
AppPhotoSizeOutputComponent
建立一個 reactive form
來輸入照片的 width 和 height。 我想當 width
FormControl 的值至少為 700 時訂閱 OutputRef
。
isBigPortrait = outputFromObservable(this.form.controls.width.valueChanges
.pipe(
debounceTime(300),
map((v) => v >= 700),
));
isBigPortrait
是一個 OutputRef
,當寬度至少為 700 時發出 true,否則發出 false。
import { Component, ChangeDetectionStrategy, signal, viewChild, effect, computed } from '@angular/core';
import AppPhotoOutputComponent from './photo-output.component';
import AppPhotoSizeOutputComponent from './photo-output-size-output.component';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-photo-wrapper-output',
standalone: true,
imports: [AppPhotoSizeOutputComponent, AppPhotoOutputComponent, FormsModule],
template: `
<div class="photo-output-wrapper">
<app-photo-size-output #size />
@if(showName()) {
<div>
Name: <input [(ngModel)]="name" />
</div>
}
</div>
`,
})
export default class AppPhotoWrapperOutputComponent {
size = viewChild.required(AppPhotoSizeOutputComponent);
showName = signal(false);
name = signal('Luke');
constructor() {
effect(() => {
this.size().isBigPortrait.subscribe((value) => this.showName.set(value));
});
}
}
AppPhotoWrapperOutputComponent
組件匯入 AppPhotoSizeOutputComponent
組件以顯示 reactive form。 viewChild
函數查詢 AppPhotoSizeOutputComponent
組件以存取 isBigPortrait
output。
constructor() {
effect(() => {
this.size().isBigPortrait.subscribe((value) => this.showName.set(value));
});
}
在組件的 effect
中,訂閱了 isBigPortrait
來設定 showName
signal 的值。
@if(showName()) {
<div>Name: <input [(ngModel)]="name" /></div>
}
showName
訊號有條件地在 HTML 範本中顯示輸入欄位,以供使用者輸入名稱並更新 name
訊號。
@Component({
selector: 'app-photo-size-output',
standalone: true,
imports: [ReactiveFormsModule, FormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="formSubmitSub.next()">
… width and height form controls …
<button type="submit" [disabled]="this.form.invalid">Change photo</button>
</form>
`,
})
export default class AppPhotoSizeOutputComponent {
formSubmitSub = new Subject<void>();
submittedValues = outputFromObservable(this.form.events.pipe(
filter((e) => e instanceof FormSubmittedEvent),
filter((e) => e.source.valid),
map(({ source }) => ({
timestamp: new Date().toISOString(),
value: source.value as { width: number; height: number }
})),
));
}
表單事件過濾 FormSubscribedEven
以驗證事件來源。當表單資料有效時,map
運算子將 Observable
對應到目前時間和資料。此函數將新的 Observable
轉換為 output
並將其指派給 submittedValues
變數。
@Component({
selector: 'app-photo-wrapper-output',
standalone: true,
imports: [AppPhotoSizeOutputComponent, AppPhotoOutputComponent, FormsModule],
template: `
<div class="photo-output-wrapper">
<p>{{ text() }}</p>
@if (this.formObj().timestamp) {
<p>Form submitted at {{ this.formObj().timestamp }}.</p>
}
<app-photo-size-output />
<app-photo-output [img]="this.formObj().url" />
</div>
`,
})
export default class AppPhotoWrapperOutputComponent {
formObj = signal({
url: 'https://picsum.photos/300/200',
timestamp: ''
})
size = viewChild.required(AppPhotoSizeOutputComponent);
text = computed(() => {
const isShowName = this.showName();
const requester = isShowName ? `${ this.name() } wants ` : '';
return `${requester}${ this.formObj().url }`;
});
constructor() {
effect(() => {
this.size().submittedValues.subscribe(({ timestamp, value: { width, height }}) => {
const url = `https://picsum.photos/${width}/${height}?random=${Date.now()}`;
this.formObj.set({
url,
timestamp,
});
});
});
}
}
在 AppPhotoWrapperOutputComponent
組件的 effect
中,訂閱 submittedValues
OutputRef來提取時間、寬度和高度來建立圖像URL。 圖像 URL 和時間會覆蓋 formObj
signal 以更新 HTML 範本。
<div class="photo-output-wrapper">
<p>{{ text() }}</p>
@if (this.formObj().timestamp) {
<p>Form submitted at {{ this.formObj().timestamp }}.</p>
}
<app-photo-size-output />
<app-photo-output [img]="this.formObj().url" />
</div>
text = computed(() => {
const isShowName = this.showName();
const requester = isShowName ? `${ this.name() } wants ` : '';
return `${requester}${ this.formObj().url }`;
});
此範本在第一個段落元素中顯示名稱和圖像 url。當表單提交的時間不是空字串時,第二個段落元素顯示 ISO 日期字串。 該 url 也是 AppPhotoOutputComponent
組件的 input
,用於顯示新影像。
outputFromObservable
將 Observable
轉換為 OutputRef
,以便可以訂閱 (subscribe) 該值。outputFromObservable
駐留在 rxjs-interop
套件中,就像 toSignal
和 toObservable
一樣outputFromObservable
函數建立 OutputRef
時,可以透過程式訂閱它。鐵人賽的第 25 天到此結束。